#include<stdio.h>
#include<stdlib.h>
#include<string.h>
#include<sys/types.h>
#include<unistd.h>
#include<pthread.h>
#include<semaphore.h>
#include<time.h>

typedef struct node{
	int data;
	struct node *next;

}* NODE;

NODE head = NULL;// 单链表的头指针；
// 开一个单链表的结构体来管理生产的产品；

sem_t sem_producer;
sem_t sem_consumer;

// 生产者线程task;

void * producer(void* arg){
	NODE pNode = NULL; // 声明并初始化为空指针，防止指针空悬；
	int i=0; // 这里下面的这个循环可以写成死循环 while(1){ loop-body }；
	while(i<100){
		pNode = (NODE)malloc(sizeof(struct node)); // 动态在堆中开辟一块内存给单链表节点；
		if(NULL==pNode){ // 动态开辟内存是如果堆空间不足会返回一个空指针，这里是处理对应的异常状况的；
			perror("memory limit malloc error");
			exit(-1);
		}
		pNode->data = i;
		printf("Produce:[%d]\n",pNode->data);
		sem_wait(&sem_producer);
		// 生产者信号量减一；
		pNode->next = head;
		head = pNode; // 头插法构建流水线；
		sem_post(&sem_consumer);
		// 消费者信号量加一；
		srand(time(0)); //这样根据对应的秒来设置随机数种子；
		sleep(rand()%3);// 根据产生的随机值停顿0-2秒；
		++i; // 给控制循环的变量加1；

	}
}
// 消费者线程task

void * consumer(void *arg){
	NODE pNode = NULL;
	int i = 0;
	while (i<100) { 
		sem_wait(&sem_consumer);
		printf("consumer:[%d]\n",head->data);
		pNode = head;
		head = head->next; // 这里的pNode 变量是用来指向已经被消费的节点，从当前的库存产线中进行释放；
		sem_post(&sem_producer);
		free(pNode);// 对存在堆中节点的程序注意在不需要再次使用时一定要释放；avoid memory leak;
		pNode= NULL;
		srand(time(0));
		sleep(rand()%3);
		++i;
	}
}


// 主程序入口；
int main(){
	int ret;
	pthread_t thread1, thread2;
	sem_init(&sem_producer,0,5);// 给生产者最小0，最大产量为5；
	sem_init(&sem_consumer,0,0);
	ret = pthread_create(&thread1,NULL,producer,NULL); // 空参
	if(ret!=0){
		printf("thread create failed the error is [%s]\n",strerror(ret));
		return -1;
	}
	ret = pthread_create(&thread2,NULL,consumer,NULL);
	if(ret!=0){
		printf("thread create failed the error is [%s]\n",strerror(ret));
		return -1;
	}
    pthread_join(thread1,NULL); // 阻塞等待两个线程结束
	pthread_join(thread2,NULL); 
	sem_destroy(&sem_producer);
	sem_destroy(&sem_consumer);
	return 0;



}
